home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c-part2 / 10169 < prev    next >
Encoding:
Text File  |  1996-08-05  |  9.9 KB  |  311 lines

  1. Path: hubcap.clemson.edu!hubcap!mjs
  2. From: mjs@hubcap.clemson.edu (M. J. Saltzman)
  3. Newsgroups: comp.lang.c
  4. Subject: Re: NEwbie: How to return a multi-dimensional array from function?
  5. Date: 15 Mar 96 18:59:48 GMT
  6. Organization: Clemson University
  7. Message-ID: <mjs.826916388@hubcap>
  8. References: <4hp273$8bu@news.xs4all.nl> <31404CE9.1A4A@mc.net> <mjs.826298629@hubcap> <4ib78s$6gv@news.bridge.net>
  9. NNTP-Posting-Host: hubcap.clemson.edu
  10. X-Newsreader: NN version 6.5.0 #1
  11.  
  12. psycho@bridge.net (Gary Thompson) writes:
  13.  
  14. |mjs@hubcap.clemson.edu (M. J. Saltzman) wrote:
  15.  
  16. |>Sean Park <spark@mc.net> writes:
  17.  
  18. |>>Anthony Moendir wrote:
  19. |>>> 
  20. |>>> I want to return a multidimensional array from a function,
  21. |>>> something like this:
  22. |>>> 
  23. |>>> char *Foo();
  24. |>>> int main()
  25. |>>> {
  26. |>>>  char tmp[10][5];
  27. |>>>  tmp=Foo();
  28. |>>> }
  29. |>>> 
  30. |>>> char *Foo()
  31. |>>> {
  32. |>>>  char tmp[10][5];
  33.  
  34. |>Two things: (1) If you are going to use the value of tmp outside of Foo(),
  35. |>then you need to declare
  36. |>    static tmp[10][5];
  37. |>Otherwise the storage may disappear when you return from Foo().
  38.  
  39. |No, declaring it as static will only retain the value in the foo() function, if
  40. |you call it more than once.  You CANNOT use a value declared in a subfunction in
  41. |MAIN.  You would have to remove both TMP declarations from MAIN and FOO and
  42. |declare the thing globally outside of main.
  43.  
  44. But we return the address at the end of the function.  If the variable
  45. is static, the pointer in main() can be used to access that memory
  46. between calls to Foo().
  47.  
  48. |>>> //do something
  49. |>>> return(tmp);
  50. |>         ^   ^ Parentheses not required here.
  51. |>>> }
  52.  
  53. |Makes no difference.  They are compiled the same either way.  Who would care
  54. |about saving two bytes code space?
  55.  
  56. I didn't say they were forbidden, only unnecessary.  I point it out
  57. only because some people don't seem to realize it.
  58.  
  59. |>(2) When you use tmp in an expression, the compiler replaces the array
  60. |>name with a constant pointer to its first element.  (Among other
  61. |>consequences, this means that arrays can't appear on the left side of
  62. |>assignments.)  In this case, the first element has type
  63. |>array-of-15-chars, so a pointer to it is a
  64. |>pointer-to-array-of-15-chars.  That's the type that the function
  65. |>should return, and the type of the variable that you assign it to.
  66. |>Thus, Foo() needs to be declared
  67.  
  68. |>    char (*Foo())[5];
  69. |>and the tmp in main() should be 
  70. |>    char (*tmp)[5];
  71.  
  72. |WHAT???  You just declared five FUNCTION pointers pointed to nothing!  If you
  73. |ran that, you'd get a NULL pointer error.  It wouldn't even run!  Again,
  74. |declaring char (*tmp)[5] is declaring an array of five pointers to chars and
  75. |assigning them to nothing... which will give you a NULL POINTER error AGAIN.
  76.  
  77. The declaration of Foo is parsed as follows (inside-out, postfix-first,
  78. respecting parentheses):
  79.  
  80.     Foo is a function returning a pointer to an array of 5 chars.
  81.  
  82. Which is what I said it was (except I mistyped 5 as 15).
  83.  
  84. The declaration of the tmp in main() is:
  85.  
  86.     tmp is a pointer to an array of 5 chars.
  87.  
  88. So the assignment 
  89.  
  90.     tmp = Foo()
  91.  
  92. is legal (in that the types of the objects on both sides are the same).
  93.  
  94. BTW, If you want an array of five pointers to char, that's
  95.  
  96.     char *tmp[5];
  97.  
  98. An array of five pointers to functions returning char is
  99.  
  100.     char (*tmp[5])();
  101.  
  102. |>>>[...]
  103.  
  104. |>Another technique for getting the result back is to pass the array in as an
  105. |>argument, as is done below (with corrections).
  106.  
  107. |>>You could pass the pointer to the array to the function.  That way the 
  108. |>>function itself will fill your array addresses rather than returning 
  109. |>>redundant data.  You might try:
  110.  
  111. |>>void foo(char *tmp);
  112. |>          ^^^^^^^^^ Here, tmp should be declared char (*tmp)[5].
  113.  
  114. |That wont work.  All pointers, require either 2 or 4 bytes (I forget which).
  115.  
  116. Sometimes 2, sometimes 4, sometimes more, but it doesn't matter, since
  117. there is no type punning anywhere here; my tmp is a pointer to an array
  118. of 5 chars.
  119.  
  120. |You cannot pass data this way.  Chars only take up one byte.  If you pass an
  121. |array of chars this way, you will get scrambled data at the other end.  It will
  122. |try to interpret the passed chars as pointer addresses.  You will have to pass
  123. |an array of POINTERS for this to work.  And you cannot pass arrays of anything.
  124.  
  125. I'm not passing either a character or an array of characters (or even
  126. an array of pointers).  Again, my declaration is for a pointer to an
  127. array of 5 chars.  In this case, it's a pointer to the first array of
  128. 5 chars in an array of 10 such arrays.
  129.  
  130. |>>int main()
  131. |>>{
  132. |>>   char tmp[10][5];
  133. |>>   foo(tmp);
  134. |>>   return 0;
  135. |>>}
  136. |>>void foo(char *tmp)
  137. |>          ^^^^^^^^^ Here, tmp should be declared char (*tmp)[5].
  138.  
  139. |Ok, since you declared foo() to accept only an array of five pointers at 2 bytes
  140.  
  141. No, a pointer to an array of 5 chars.
  142.  
  143. |each, why are you passing it a single pointer?  When you pass it like foo(tmp),
  144. |you are only passing it a single pointer to the beginning of the 2d array.  You
  145. |will get one pointer and the rest garbage.
  146.  
  147. I ask for only one pointer (to an array of 5 chars), and I pass only
  148. one pointer (the address of the first array of 5 chars in an array of 10
  149. such arrays).
  150.  
  151. |Besides, you are ignoring the question.  You are passing the data TO foo, he
  152. |wants to return it from FOO.  Make it simple...  There are several ways of doing
  153.  
  154. I'm passing the address of a region of memory in main() (the 2-D
  155. array tmp) that will be modified inside the function.  That achieves the
  156. desired result, namely that the array in the calling function have the
  157. value calculated by the called function.
  158.  
  159. |it easy...
  160. |--------------------
  161. |void foo(void);
  162. |char tmp[10][5];
  163.  
  164. |void main(void)
  165.  
  166. Do you read none of the discussions here?  You need to declare main as
  167.  
  168. int main(void)
  169.  
  170. |{
  171. |    foo();
  172. |    /* do something else with tmp */
  173. |}
  174.  
  175. |void foo(void)
  176. |{
  177. |    /* do something with tmp */
  178. |}
  179. |-----------------
  180. |That example allows you to access TMP within both main and foo.  Problem is,
  181. |it's not very good with security...  tmp can be modified by ALL functions....
  182. |not recommended if it's something critical.... This is a little more complicated
  183. |but still easy...
  184.  
  185. I agree with everything you say here.  Another disadvantage of this
  186. technique is that foo() cannot modify anything *other* than the global
  187. variable tmp.  All it accomplishes is to move some code out of line.
  188.  
  189. |-----------------
  190. |void foo(char *);
  191. |main()
  192. |{
  193. |    char tmp[10][5];
  194. |    foo(tmp);
  195. |}
  196.  
  197. |char *foo(char *tmp2)
  198. |{
  199. |    /* You can access tmp2 exactly like tmp... tmp2[10][5]...etc */
  200. |}
  201. |--------------------
  202. |There are a few quirks with this also...  tmp2 is accessed exactly like tmp
  203. |because it is the same thing.  tmp and tmp2 both access the same space.  That's
  204. |why you don't have to return anything.  You can use tmp instead of tmp2, but it
  205. |makes no difference.  tmp in main is a different variable than tmp in foo.  I
  206. |like to use different variable names for the sake of clarity.
  207.  
  208. No, no, no!  tmp and tmp2 are not accessed the same way at all.  tmp
  209. is an array of 10 arrays of 5 chars.  When the name of the array is
  210. used as a value (as in the call to foo), it is replaced by a pointer
  211. to the first element of the first array, in this case, a pointer to
  212. the first of the ten arrays of 5 chars.  The type of this value is
  213. (char (*)[5]) (i.e., pointer to array of 5 chars).  This is not in any
  214. way the same as a pointer to a single char, like tmp2.
  215.  
  216. Think about this:  tmp2[3] is a character, but tmp[3] is an array of 5
  217. characters.  You can't write 
  218.  
  219.     tmp2[3] = tmp[3];
  220.  
  221. (even if both were in scope).  You have to pick one of the five characters
  222. in tmp[3]:
  223.  
  224.     tmp2[3] = tmp[3][3];
  225.  
  226. You can't write tmp2[3][3] at all, since tmp2[3] is not a pointer or
  227. an array.
  228.  
  229. |One problem with C is that the ONLY way you can pass the data in an array
  230. |without passing just the pointer is by using a STRUCT.  passing the name of a
  231. |struct is the only thing that will pass the data and not a pointer to the data.
  232.  
  233. But that wasn't my goal (or the original poster's).  My goal was to 
  234. pass a pointer, so that the function could modify the array whose
  235. address was passed.  Furthermore, in this case, you are not passing
  236. an array anyway--you are now passing a struct.
  237.  
  238. |If you truly want to keep your variables independant, you will have to use a
  239. |struct.
  240.  
  241. |That is... unless you want to program in C++... you can then use the first
  242. |example and declare which functions have permission to modify it.
  243.  
  244. But that's a whole different issue, not relevant to comp.lang.c.
  245.  
  246. To recap: There are essentially two ways to return a 2-D table of values
  247. from a function:
  248.  
  249. (1) Return the address of a static array.
  250.  
  251.   char  (*Foo())[5];    /* function returning pointer to array of 5 chars */
  252.  
  253.   int main()
  254.   {
  255.     char (*tmp)[5];
  256.  
  257.     tmp = Foo();
  258.     /* use tmp--note that you can access tmp[i][j], just as you can 
  259.        access tmp2[i][j] inside the body of Foo(), and in fact, it is
  260.        tmp2[i][j] that is accessed  */
  261.  
  262.     return 0;
  263.   }
  264.  
  265.   char (*Foo())[5]
  266.   {
  267.     static char tmp2[10][5];
  268.  
  269.     /* do something to tmp2 */
  270.  
  271.     return tmp2;    /* return address of first of tmp2's arrays of 5 chars,
  272.                just as if we'd written return &tmp2[0] */
  273.   }
  274.  
  275.  
  276. (2) Pass the address of an array as a parameter and modify the array
  277. it points to.
  278.  
  279.   void Foo(char (*)[5]);    /* function taking pointer to array of 5 chars */
  280.  
  281.   int main()
  282.   {
  283.     char tmp[10][5];
  284.  
  285.     Foo(tmp);    /* pass address of first of tmp's arrays of 5 chars 
  286.            just as if we'd written Foo(&tmp[0]) */
  287.     return 0;
  288.   }
  289.  
  290.   void Foo(char (*tmp2)[5])
  291.   {
  292.     /* do something to tmp2--note that now you can access tmp2[i][j]
  293.        here, just as you access tmp[i][j] in main, and in fact, it
  294.        is tmp[i][j] that is accessed, if you call Foo(tmp) */
  295.   }
  296.  
  297. Here, we could have declared Foo equivalently as 
  298.  
  299.     void Foo(tmp2[][5]);
  300.  
  301. Note that these are exactly the same ideas that we use if we want to
  302. return a 1-D array of values.  In fact, we *are* returning a 1-D array
  303. of values; it's just that each value is an array of 5 chars.
  304.  
  305. Is that clear, now?  If not, may I recommend a careful perusal of the
  306. relevant parts of the FAQ?
  307. -- 
  308.         Matthew Saltzman
  309.         Clemson University Math Sciences
  310.         mjs@clemson.edu
  311.